home *** CD-ROM | disk | FTP | other *** search
/ MacHack 1993 / MacHack 1993.toast / MacHack™ 1987-1992 / MacHack™ '92 / Other stuff / Cool Debugging Tools / Leaks dcmd / Leaks.a < prev    next >
Encoding:
Text File  |  1990-10-18  |  22.2 KB  |  475 lines  |  [TEXT/MPS ]

  1. ; Leaks.a
  2. ;
  3. ;        Copyright © 1990 by Apple Computer, Inc., all rights reserved.
  4. ;
  5. ;    by Bo3b Johnson x4625    10/9/90
  6. ;        
  7. ;    This file is the assembly routines I need in order to interface to the toolbox in
  8. ;    a sensible fashion.  The OS is funky, and there is no good way to do these things
  9. ;    from Pascal or C.  The use of INLINE code is OK, but not always enough.
  10. ;
  11. ;    In order to fashion a good patch to NewPtr/NewHandle, I need to use PC
  12. ;    relative addressing.  The basic problem is that there is no good way to get back
  13. ;    to the dcmd globals, while the patch is executing.  Certainly you can't rely upon
  14. ;    A5, A6, or A7; and no other registers are available for use.  I could stick something
  15. ;    into AppParms, CurApName, or a number of other atrocities, but I don't own
  16. ;    any of those spots, and could quite easily end up competing with some other code
  17. ;    for the spot.  In the past, this code patched the Chain trap, since it is not in use,
  18. ;    but that has competition problems too.  If I use PC-relative addressing to get to
  19. ;    some global information, then this becomes self-modifying code, since I will
  20. ;    change the code in this block, using a piece of code space as variable storage.  
  21. ;    This seems the most risk-free version for now, notwithstanding certain strident
  22. ;    if misguided warnings from various groups.  It's not enough to tell me not to
  23. ;    do something, you have to give me an alternative.  If you have a better alternative,
  24. ;    by all means let me know.
  25. ;
  26. ;    I patch the four traps of NewPtr/DisposPtr, NewHandle/DisposHandle; and all
  27. ;    these pieces are almost identical.  The only difference really is the variable being
  28. ;    used.  The basic idea is to have one routine to save off the old address, gotten from
  29. ;    an NGetTrapAddress; and to save that into the code here, using PC-relative addressing.
  30. ;    The trap is patched by the Pascal code, and whenever it gets called, it will call here
  31. ;    to have us call through the old version of the trap.  That way it gets control before
  32. ;     the trap executes, but I still drive the old code. 
  33. ;
  34. ;    Realize that the code in this file is getting run mostly during the normal operation
  35. ;    of the Mac.  The stack is whatever the current app is using, as well as that A5 world,
  36. ;    and stack crawl.  I don't where I'll be so, I can't rely on those things.  This
  37. ;    is very different from the code in the dcmd, where it knows the stack it is using is
  38. ;    in Macsbug, and very small.  Pieces of this code will get run a tiny bit from the dcmd,
  39. ;    since it will call here in order to get access to my global variables, the treeTop, emptyQ,
  40. ;    and activeState.  
  41. ;
  42. ;    One of the goals in this file is to avoid needing an asm version of the record structure
  43. ;    I use.  I really just want to pass around pointers, so that this file can be a little
  44. ;    more independent of the basic structures.  One of the things that's get used though is
  45. ;    the size of the Stack Crawl array that I pass around.  There is a constant here, but
  46. ;    if it changes in the Pascal code, it should change here.
  47.  
  48. ;     By the way, this file looks best if viewed in Palatino 12.
  49.  
  50.     
  51.             include        'SysErr.a'
  52.             include        'SysEqu.a'
  53.             include        'Traps.a'
  54.  
  55.             proc
  56.             import        AddNewBlock, KillOldBlock
  57.  
  58. ; ——————————————————————————————————————————————————————————————————————————————————————————————————
  59. ;
  60. ; Storage for the old patch addresses, used to call through once the patch code executes.
  61. ;    These are essentially globals, used by the asm code.  They are specifically not exported,
  62. ;    so that the Pascal code cannot access them directly.  There are a number of interface
  63. ;    routines I set up so that Pascal can get and set them, but has to go through this file.
  64. ;    You know, sort of object like.
  65. ;
  66. pOldNewPtr                dc.l        0
  67. pOldNewHandle        dc.l        0
  68. pOldDisposPtr            dc.l        0
  69. pOldDisposHandle        dc.l        0
  70.  
  71. pEmptyQ                    dc.l        0                    ; list of empty elements available.
  72. pTreeTop                dc.l        0                    ; list of active elements.
  73. pActive                    dc.w        0                    ; whether to watch blocks or not.
  74.  
  75.  
  76. saveEm                    reg        a0-a3/d0-d3        ; a few to keep, since I'm a patch.
  77. saveEmSize                EQU        (8*4)                ; 8 registers times 4 bytes each.
  78.  
  79.  
  80. kCrawlArraySize        EQU        8                    ; number of stack crawl elements to do.
  81.  
  82.  
  83. ; ——————————————————————————————————————————————————————————————————————————————————————————————————
  84. ; When I'm am setting up the world, I call NGetTrapAddress to get the old version
  85. ; of the traps.  I need to save that dude off so I can get back there when needed. 
  86. ; This routine is a handy interface to the high-level world, isolating this asm junk
  87. ; from the code.   All these routines are the same, just a different variable being affected.
  88. ; This hunk uses the PC-Relative addressing mode in order to get the address of the
  89. ; variable being set.  This allows the code to function without any explicit global
  90. ; space, since the code acts like globals here.
  91. ; The interface is:
  92. ;    PROCEDURE  SetOldNewPtr (address: LongInt);
  93. ;    PROCEDURE  SetOldNewHandle (address: LongInt);
  94. ;    PROCEDURE  SetOldDisposPtr (address: LongInt);
  95. ;    PROCEDURE  SetOldDisposHandle (address: LongInt);
  96.  
  97.             export        SetOldNewPtr, SetOldNewHandle, SetOldDisposPtr, SetOldDisposHandle
  98.  
  99. SetOldNewPtr
  100.             LEA            pOldNewPtr,A1            ; the variable to be setting
  101.             BRA.S        Common                    ; do common stuffing code.
  102. SetOldNewHandle
  103.             LEA            pOldNewHandle,A1        ; the variable to set
  104.             BRA.S        Common                    ; do common stuffing code.
  105. SetOldDisposPtr
  106.             LEA            pOldDisposPtr,A1            ; the variable to do
  107.             BRA.S        Common                    ; do common stuffing code.
  108. SetOldDisposHandle
  109.             LEA            pOldDisposHandle,A1        ; the variable 
  110. Common        
  111.             MOVE.L        (SP)+,A0                        ; get the return address.
  112.             MOVE.L        (SP)+,(A1)                    ; save it, pulling parameter too.
  113.             JMP            (A0)                            ; it's saved, return to high-level.
  114.  
  115.  
  116. ; ——————————————————————————————————————————————————————————————————————————————————————————————————
  117. ;    These routines are used by the dcmd in order to get to the global variables, to find the
  118. ;    treeTop, emptyQ, and activeState.  They aren't used by the b-tree management stuff,
  119. ;    since I always pass these variables to those routines.  In addition, the patch code above
  120. ;    doesn't use this code, since it has direct access to the variables, and does so, using the
  121. ;    pc-relative approach.  
  122. ;
  123. ;
  124. ; GetTreeInfo:
  125. ;    When desired, the pascal callers may need to get the tree info in order to be able to drive
  126. ;    the trees for info display reasons.  This is an interface to let them see us, but I get to
  127. ;     pass back the info, rather than have them groping around directly for the data.
  128. ;        FUNCTION GetTreeTop: TrackEntryPtr;     EXTERNAL;
  129. ;        FUNCTION GetEmptyQ: TrackEntryPtr;     EXTERNAL;
  130. ;        FUNCTION    TrackActive: Boolean;    EXTERNAL;
  131.  
  132.             export        GetTreeTop, GetEmptyQ, TrackActive
  133.  
  134. ; GetTreeTop:
  135. ;    This routine just returns the tree top as a parameter.  The assembly stuff just grabs
  136. ;    these guys directly when it needs to, but the Pascal code comes through this interface
  137. ;    to get the references.
  138. ;        FUNCTION GetTreeTop: TreeEntryPtr;     EXTERNAL;
  139.  
  140. GetTreeTop
  141.             MOVE.L        pTreeTop,4(SP)                ; stuff the return result (pc-relative for source.)
  142.             RTS                                            ; and return to caller.
  143.  
  144. ; GetEmptyQ:
  145. ;        FUNCTION GetEmptyQ: TreeEntryPtr;     EXTERNAL;
  146.  
  147. GetEmptyQ
  148.             MOVE.L        pEmptyQ,4(SP)                ; stuff the return result (pc-relative for source.)
  149.             RTS                                            ; and return to caller.
  150.  
  151. ; TrackActive:
  152. ;    Just return the active state as a boolean.  This is whether I am actively watching
  153. ;    and recording block addresses or not.  I always want to be able to turn it off sometimes,
  154. ;    since I want to save a given state for viewing.  
  155. ;        FUNCTION    TrackActive: Boolean;    EXTERNAL;
  156.  
  157. TrackActive
  158.             MOVE.W    pActive,4(SP)                ; stuff function result (pc-relative source).
  159.             RTS                                            ; and return to caller. 
  160.             
  161.  
  162. ; ——————————————————————————————————————————————————————————————————————————————————————————————————
  163. ; As part of the interface to the dcmd, I have setting routines too, to set the variables
  164. ; to a known state.  This way I don't have to put that sort of tree-based init code in 
  165. ; assembly.  I need to turn it on and off using the dcmd, so it will call here to do so.
  166. ;
  167. ;        PROCEDURE SetTreeTop (address: TrackEntryPtr);        EXTERNAL;
  168. ;        PROCEDURE SetEmptyQ (address: TrackEntryPtr);        EXTERNAL;
  169. ;        PROCEDURE SetActive (state: Boolean);        EXTERNAL;
  170.  
  171.  
  172.             export        SetTreeTop, SetEmptyQ, SetActive
  173.  
  174. ;        PROCEDURE SetTreeTop (address: TrackEntryPtr);        EXTERNAL;
  175.  
  176. SetTreeTop
  177.             MOVE.L        (SP)+,A0                        ; the return address, for safe keeping.
  178.             LEA            pTreeTop,A1                ; the variable I need to set, pc-relative.
  179.             MOVE.L        (SP)+,(A1)                    ; save off the new variable, clearing parameter.
  180.             JMP            (A0)                            ; and return to caller.
  181.             
  182. ;        PROCEDURE SetEmptyQ (address: TrackEntryPtr);        EXTERNAL;
  183.  
  184. SetEmptyQ
  185.             MOVE.L        (SP)+,A0                        ; the return address, for safe keeping.
  186.             LEA            pEmptyQ,A1                ; the variable I need to set, pc-relative.
  187.             MOVE.L        (SP)+,(A1)                    ; save off the new variable, clearing parameter.
  188.             JMP            (A0)                            ; and return to caller.
  189.             
  190. ;        PROCEDURE SetActive (state: Boolean);        EXTERNAL;
  191.  
  192. SetActive
  193.             MOVE.L        (SP)+,A0                        ; the return address, for safe keeping.
  194.             LEA            pActive,A1                    ; the variable I need to set, pc-relative.
  195.             MOVE.W    (SP)+,(A1)                    ; save off the new state, clearing parameter.
  196.             JMP            (A0)                            ; and return to caller.
  197.             
  198.             
  199. ; ——————————————————————————————————————————————————————————————————————————————————————————————————
  200. ;    This is the patch code for the various pieces I patch.  This is in assembly
  201. ;     since I need to preserve registers and do some other low level jacking around,
  202. ;    like trying to do a stack crawl whenever I allocate blocks.  These routines are the
  203. ;     actual code that gets called when one of the four traps is executed.
  204.  
  205. ; WatchNewPtr:
  206. ;    The routine to patch the NewPtr trap, and I get first dibs at getting the info I want
  207. ;    out of the registers.  I need to save off the size to start with, since D0 gets pounded
  208. ;    to the return result once I actually call the old NewPtr code.  Once I have that
  209. ;    safely stowed on the stack, I call the old code, using the PC-relative addressing in
  210. ;    order to get the old address that I'm supposed to call.  This was set up when the
  211. ;    dcmd called me to save off the value.  When that old NewPtr junk returns, I get
  212. ;     the actual address allocated, if enough memory, then do the funky stack crawl,
  213. ;    and finally pass all these tidbits off to the AddNewBlock routine.
  214.  
  215.             export        WatchNewPtr
  216.             
  217.  
  218. WatchNewPtr
  219.             MOVE.L        D0,-(SP)                        ; save size off.
  220.  
  221. ; First, allocate the pointer.
  222.  
  223.             PEA            @1                            ; coming back here, as return address.
  224.             MOVE.L        pOldNewPtr,-(SP)            ; the old address I am going to,
  225.             RTS                                            ; JMP there, when it RTSes I come back
  226. @1                                                        ; --here.
  227.  
  228. ; A0 is set up as the handle to track, add it to the b-tree list.  If it is NIL, meaning the
  229. ; NewPtr failed, then don't add it to the list, it cannot be disposed.  I do the save of
  230. ; registers here, after the allocation of memory, so that I can save the registers as they
  231. ; are coming out of the call, not going in.
  232.  
  233.             MOVEM.L    saveEm,-(SP)                ; save registers in trap patches.
  234.  
  235.             MOVE.W    pActive,D0                    ; is it turned on to watch?
  236.             BEQ.S            @exit                            ; if not, skip saving.
  237.             CMP.L        #0,A0                            ; is it a NIL return?
  238.             BEQ.S            @exit                            ; if so, bag it.
  239.             
  240. ; Now I am supposed to track the block that was just allocated, so I need to call the AddNewBlock
  241. ; routine in order to tie it in.  In order to set up the parameters for the call, I have to do a stack
  242. ; crawl, to get the previous return addresses.  The problem of course is that the stack crawl has
  243. ; no known length, and is often less than kCrawlArraySize.  So... I have to look at each one as
  244. ; it is created, and make sure that it is valid; and continue no farther when I find a bad one.  If
  245. ; I go on, I risk dying from a bus error, since the addresses won't chain nicely in all cases.  I will call
  246. ; the routine, where the stackToAdd is an array, built on the stack:
  247. ;    PROCEDURE AddNewBlock (addressToAdd, sizeToAdd: LongInt; stackToAdd: StackArray;
  248. ;        VAR treeTop, emptyQ: TrackEntryPtr);
  249.  
  250.             MOVE.L        saveEmSize(SP),D1        ; retrieve size parameter.
  251.             
  252.             MOVEQ        #kCrawlArraySize-1,D0    ; for 8 times in DBRA loop.
  253. @Clr
  254.             CLR.L            -(SP)                            ; make a stack crawl array on the stack,
  255.             DBRA        D0,@Clr                        ; by looping 8 times. (all zeroed.)
  256.             
  257.             MOVE.L        SP,A1                            ; the address of the stack crawl array.
  258.             
  259. ; Now with that array built and zeroed I want to add the parameters to the stack.  If I have to 
  260. ; bail out of the stack crawl before it hits all kCrawlArraySize elements, then the rest have 
  261. ; already been zeroed, which marks them as non-valid.  I'm going to set up for the call 
  262. ; here, so the registers are freed up for doing the funky stack crawl checks.
  263.  
  264.             MOVE.L        A0,-(SP)                        ; addressToAdd: block being added to tracker.
  265.             MOVE.L        D1,-(SP)                        ; sizeToAdd: size of block being tracked.
  266.             MOVE.L        A1,-(SP)                        ; stackToAdd: the address of the array of crawls.
  267.             PEA            pTreeTop                    ; treeTop: bTree top.      (as var parameters)
  268.             PEA            pEmptyQ                        ; emptyQ: top of empties list. 
  269.             
  270. ; All the parameters are set up, do the stack crawl checks.  This will modify the stackToAdd
  271. ; array, for each crawl that is valid.  The stackToAdd is still in A1.
  272.  
  273.             MOVE.L        A6,A0                        ; get stack frame head.
  274.             MOVEQ        #kCrawlArraySize-1,D0    ; doing a loop for 8 stack crawls.
  275.             
  276. @Frame
  277.             CMP.L         A7,A0                        ; make sure it is greater than a7
  278.             BLO.S            @noFrame                    ; skip any more if not valid.
  279.             CMP.L        CurStackBase,A0            ; make sure it is less than base of stack,
  280.             BHI.S            @noFrame                    ; If not, skip checking rest of crawl.
  281.  
  282. ; It's a valid frame, go ahead and save off the return address in the array.  A1 is the address of
  283. ; the next array element to set.  It is autoincremented to the next entry each time I can stow a
  284. ; valid pc return address.
  285.  
  286.             MOVE.L        4(A0),(A1)+                    ; the pc return address for this frame, into Array.
  287.             
  288.             MOVE.L        (A0),A0                        ; next stack frame back. (as a big chain)
  289.             DBRA        D0,@Frame                    ; loop for all 8.
  290.             
  291. ; If there was a bogus frame in there somewhere, I bailed out to here.  All the rest of the stackArray
  292. ; entries are nil though, so it doesn't matter, I'll just call the AddNewBlock anyway.  I got here if
  293. ; all the entries were valid, too, and just fell out of the DBRA loop.  
  294.  
  295. @noFrame
  296.             BSR            AddNewBlock                ; track a new block on heap.
  297.             ADD.L        #kCrawlArraySize*4,SP    ; kill the crawl array from stack.
  298. @exit
  299.             MOVEM.L    (SP)+,saveEm                ; save registers in trap patches.
  300.             ADDQ        #4,SP                            ; kill size that was saved.     (from D0)
  301.             RTS                                            ; Return to caller of NewPtr.
  302.  
  303.  
  304. ; ——————————————————————————————————————————————————————————————————————————————————————————————————
  305. ; WatchDisposPtr:
  306. ;    This routine is my patch to DisposPtr, and it watches the address being passed in
  307. ;    A0 to see if it is one of the blocks I am tracking in the table.  If it is, I release that
  308. ;    record from my b-Tree code, since it is obviously not a leak when it is released.  
  309. ;    If the block is not found, then I just skip it too, since there are presumably a 
  310. ;    ton of blocks that I didn't see being allocated, since I'm not watching from the 
  311. ;    start of the system.  This is a head patch, since I do my thing, then jump to
  312. ;    the old routine.  When that routine finishes, it will call RTS and go back to
  313. ;    wherever I was called from.
  314.  
  315.             export        WatchDisposPtr
  316.             
  317. WatchDisposPtr
  318.             MOVEM.L    saveEm,-(SP)                ; save registers in trap patches.
  319.  
  320.             MOVE.W    pActive,D0                    ; is it turned on to watch?
  321.             BEQ.S            CallOldDisposePtr            ; if not, skip saving.
  322.             
  323.             MOVE.L        A0,-(SP)                        ; block being added to tracker.
  324.             PEA            pTreeTop                    ; bTree top.      (as var parameters)
  325.             PEA            pEmptyQ                        ; top of empties list. 
  326.             BSR            KillOldBlock                ; get it out of my dang table
  327.  
  328. CallOldDisposePtr
  329.             MOVEM.L    (SP)+,saveEm                ; save registers in trap patches.
  330.  
  331.             MOVE.L        pOldDisposPtr,-(SP)        ; get address of old routine.
  332.             RTS                                            ; and jump there.
  333.                                                             ; when it RTSes, I'll go back to the caller.
  334.                         
  335.  
  336. ; ——————————————————————————————————————————————————————————————————————————————————————————————————
  337.  
  338. ; WatchNewHandle:
  339. ;    The routine to patch the NewHandle trap, and I get first dibs at getting the info I want
  340. ;    out of the registers.  I need to save off the size to start with, since D0 gets pounded
  341. ;    to the return result once I actually call the old NewHandle code.  Once I have that
  342. ;    safely stowed on the stack, I call the old code, using the PC-relative addressing in
  343. ;    order to get the old address that I'm supposed to call.  This was set up when the
  344. ;    dcmd called me to save off the value.  When that old NewHandle junk returns, I get
  345. ;     the actual address allocated, if enough memory, then do the funky stack crawl,
  346. ;    and finally pass all these tidbits off to the AddNewBlock routine.  The AddNewBlock
  347. ;    doesn't differentiate between pointers being watched or handles being watched, it is
  348. ;     just an address. 
  349.  
  350.             export        WatchNewHandle
  351.             
  352.  
  353. WatchNewHandle
  354.             MOVE.L        D0,-(SP)                        ; save size off.
  355.  
  356. ; First, allocate the handle.
  357.  
  358.             PEA            @1                            ; coming back here, as return address.
  359.             MOVE.L        pOldNewHandle,-(SP)        ; the old address I am going to,
  360.             RTS                                            ; JMP there, when it RTSes I come back
  361. @1                                                        ; --here.
  362.  
  363. ; A0 is set up as the handle to track, add it to the b-tree list.  If it is NIL, meaning the
  364. ; NewHandle failed, then don't add it to the list, it cannot be disposed.  I do the save of
  365. ; registers here, after the allocation of memory, so that I can save the registers as they
  366. ; are coming out of the call, not going in.
  367.  
  368.             MOVEM.L    saveEm,-(SP)                ; save registers in trap patches.
  369.  
  370.             MOVE.W    pActive,D0                    ; is it turned on to watch?
  371.             BEQ.S            @exit                            ; if not, skip saving.
  372.             CMP.L        #0,A0                            ; is it a NIL return?
  373.             BEQ.S            @exit                            ; if so, bag it.
  374.             
  375. ; Now I am supposed to track the block that was just allocated, so I need to call the AddNewBlock
  376. ; routine in order to tie it in.  In order to set up the parameters for the call, I have to do a stack
  377. ; crawl, to get the previous return addresses.  The problem of course is that the stack crawl has
  378. ; no known length, and is often less than kCrawlArraySize.  So... I have to look at each one as
  379. ; it is created, and make sure that it is valid; and continue no farther when I find a bad one.  If
  380. ; I go on, I risk dying from a bus error, since the addresses won't chain nicely in all cases.  I will call
  381. ; the routine, where the stackToAdd is an array, built on the stack:
  382. ;    PROCEDURE AddNewBlock (addressToAdd, sizeToAdd: LongInt; stackToAdd: StackArray;
  383. ;        VAR treeTop, emptyQ: TrackEntryPtr);
  384.  
  385.             MOVE.L        saveEmSize(SP),D1        ; retrieve size parameter.
  386.             
  387.             MOVEQ        #kCrawlArraySize-1,D0    ; for 8 times in DBRA loop.
  388. @Clr
  389.             CLR.L            -(SP)                            ; make a stack crawl array on the stack,
  390.             DBRA        D0,@Clr                        ; by looping 8 times. (all zeroed.)
  391.             
  392.             MOVE.L        SP,A1                            ; the address of the stack crawl array.
  393.             
  394. ; Now with that array built and zeroed I want to add the parameters to the stack.  If I have to 
  395. ; bail out of the stack crawl before it hits all kCrawlArraySize elements, then the rest have 
  396. ; already been zeroed, which marks them as non-valid.  I'm going to set up for the call 
  397. ; here, so the registers are freed up for doing the funky stack crawl checks.
  398.  
  399.             MOVE.L        A0,-(SP)                        ; addressToAdd: block being added to tracker.
  400.             MOVE.L        D1,-(SP)                        ; sizeToAdd: size of block being tracked.
  401.             MOVE.L        A1,-(SP)                        ; stackToAdd: the address of the array of crawls.
  402.             PEA            pTreeTop                    ; treeTop: bTree top.      (as var parameters)
  403.             PEA            pEmptyQ                        ; emptyQ: top of empties list. 
  404.             
  405. ; All the parameters are set up, do the stack crawl checks.  This will modify the stackToAdd
  406. ; array, for each crawl that is valid.  The stackToAdd is still in A1.
  407.  
  408.             MOVE.L        A6,A0                        ; get stack frame head.
  409.             MOVEQ        #kCrawlArraySize-1,D0    ; doing a loop for 8 stack crawls.
  410.             
  411. @Frame
  412.             CMP.L         A7,A0                        ; make sure it is greater than a7
  413.             BLO.S            @noFrame                    ; skip any more if not valid.
  414.             CMP.L        CurStackBase,A0            ; make sure it is less than base of stack,
  415.             BHI.S            @noFrame                    ; If not, skip checking rest of crawl.
  416.  
  417. ; It's a valid frame, go ahead and save off the return address in the array.  A1 is the address of
  418. ; the next array element to set.  It is autoincremented to the next entry each time I can stow a
  419. ; valid pc return address.
  420.  
  421.             MOVE.L        4(A0),(A1)+                    ; the pc return address for this frame, into Array.
  422.             
  423.             MOVE.L        (A0),A0                        ; next stack frame back. (as a big chain)
  424.             DBRA        D0,@Frame                    ; loop for all 8.
  425.             
  426. ; If there was a bogus frame in there somewhere, I bailed out to here.  All the rest of the stackArray
  427. ; entries are nil though, so it doesn't matter, I'll just call the AddNewBlock anyway.  I got here if
  428. ; all the entries were valid, too, and just fell out of the DBRA loop.  
  429.  
  430. @noFrame
  431.             BSR            AddNewBlock                ; track a new block on heap.
  432.             ADD.L        #kCrawlArraySize*4,SP    ; kill the crawl array from stack.
  433. @exit
  434.             MOVEM.L    (SP)+,saveEm                ; save registers in trap patches.
  435.             ADDQ        #4,SP                            ; kill size that was saved.     (from D0)
  436.             RTS                                            ; Return to caller of NewPtr.
  437.  
  438.  
  439. ; ——————————————————————————————————————————————————————————————————————————————————————————————————
  440. ; WatchDisposHandle:
  441. ;    This routine is my patch to DisposHandle, and it watches the address being passed in
  442. ;    A0 to see if it is one of the blocks I am tracking in the table.  If it is, I release that
  443. ;    record from my b-Tree code, since it is obviously not a leak when it is released.  
  444. ;    If the block is not found, then I just skip it too, since there are presumably a 
  445. ;    ton of blocks that I didn't see being allocated, since I'm not watching from the 
  446. ;    start of the system.  This is a head patch, since I do my thing, then jump to
  447. ;    the old routine.  When that routine finishes, it will call RTS and go back to
  448. ;    wherever I was called from.
  449.  
  450.             export        WatchDisposHandle
  451.             
  452. WatchDisposHandle
  453.             MOVEM.L    saveEm,-(SP)                ; save registers in trap patches.
  454.  
  455.             MOVE.W    pActive,D0                    ; is it turned on to watch?
  456.             BEQ.S            CallOldDisposeHandle    ; if not, skip saving.
  457.             
  458.             MOVE.L        A0,-(SP)                        ; block being added to tracker.
  459.             PEA            pTreeTop                    ; bTree top.      (as var parameters)
  460.             PEA            pEmptyQ                        ; top of empties list. 
  461.             BSR            KillOldBlock                ; get it out of my dang table
  462.  
  463. CallOldDisposeHandle
  464.             MOVEM.L    (SP)+,saveEm                ; save registers in trap patches.
  465.  
  466.             MOVE.L        pOldDisposHandle,-(SP)    ; get address of old routine.
  467.             RTS                                            ; and jump there.
  468.                                                             ; when it RTSes, I'll go back to the caller.
  469.                         
  470.             endproc
  471.  
  472. ; ——————————————————————————————————————————————————————————————————————————————————————————————————
  473.  
  474.             end
  475.